﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

/// <summary>
/// Encapsulates impersonation in code.
/// </summary>
/// <remarks>
/// <example>
/// <code>
/// using (new Impersonation("aUser", "domain", "password")
/// {
///     // code running under 'aUser' account
/// } 
/// // permissions revert back to original account
/// </code>
/// </example>
/// Code based on Brent Vermilion 9/5/2007
/// Most of this code was copied from: http://support.microsoft.com/kb/306158
/// Modifications by Stephen Smith to add using/dispose idiom and SecureString   
/// </remarks>
using System.Security.Principal;
using System.Security;
using System.Runtime.InteropServices;
public sealed class ImpersonationHelper : IDisposable
{
    WindowsImpersonationContext _impersonationContext;
    private readonly string _userName;
    private readonly string _domain;
    private readonly String _password;

    public ImpersonationHelper(String userName, String domain, String password)
    {
        _userName = userName;
        _domain = domain;
        _password = password;
        
    }


    #region win32

    // ReSharper disable InconsistentNaming
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    public string UserName
    {
        get { return _userName; }
    }

    public string Domain
    {
        get { return _domain; }
    }

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
                                        String lpszDomain,
                                        String lpszPassword,
                                        int dwLogonType,
                                        int dwLogonProvider,
                                        ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken,
                                            int impersonationLevel,
                                            ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);
    // ReSharper restore InconsistentNaming

    #endregion win32


    /// <summary>
    /// Internal class that implements the logon user.
    /// </summary>
    /// <returns></returns>
    public bool ImpersonateUser()
    {
        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(_userName, _domain, GetPassword(), LOGON32_LOGON_INTERACTIVE,
                           LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    _impersonationContext = tempWindowsIdentity.Impersonate();

                    if (_impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero) CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero) CloseHandle(tokenDuplicate);
        return false;
    }

    /// <summary>
    /// Removes the impersonation.
    /// </summary>
    public void Revert()
    {
        if (_impersonationContext != null)
        _impersonationContext.Undo();
    }

    #region Implementation of IDisposable

    ~ImpersonationHelper()
    {
        Dispose(false);
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing,
    /// releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        Revert();
        if (disposing) GC.SuppressFinalize(this);
    }

    #endregion

    private string GetPassword()
    {
        return _password;
        //IntPtr bstr = Marshal.SecureStringToBSTR(_password);
        //try
        //{
        //    return Marshal.PtrToStringBSTR(bstr);
        //}
        //finally
        //{
        //    if (bstr != IntPtr.Zero)
        //        Marshal.ZeroFreeBSTR(bstr);
        //}
    }
}
